#include <iostream>
#include <sys/epoll.h>
#include "Socket.hpp"
#include <memory>
using namespace std;
using namespace socket_ns;

class epollServer
{
    const static int gnum = 64;

public:
    epollServer(int16_t port)
        : _port(port),
          _listsockfd(make_unique<TcpSocket>()),
          _epfd(-1)
    {
        // 创建listensockfd
        InetAddr addr("0.0.0.0", _port);
        _listsockfd->BuildListenSocket(addr);
        if (_listsockfd->SockFd() < 0)
        {
            LOG(FATAL, "监听套接字创建失败\n");
        }
        LOG(DEBUG, "监听套接字创建成功  listen sockfd:%d\n", _listsockfd->SockFd());

        // 创建epoll模型
        _epfd = ::epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll模型创建失败\n");
            exit(-1);
        }
        LOG(DEBUG, "epoll模型创建成功 epfd:%d\n", _epfd);

        // 将listensockfd放到红黑树中
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listsockfd->SockFd();
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listsockfd->SockFd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "添加listen sockfd到红黑树中失败\n");
            exit(-1);
        }
        LOG(DEBUG, "添加listen sockfd到红黑树中成功\n");
    }
    // listen sock---建立连接
    void AcceptClient()
    {
        InetAddr clientaddr;
        int sockfd = _listsockfd->Accepter(&clientaddr);
        if (sockfd < 0)
        {
            LOG(ERROR, "获取新连接失败\n");
            return;
        }
        LOG(DEBUG, "获取了一个新连接 sockfd:%d  [ip:%s port:%d]\n", sockfd, clientaddr.Ip().c_str(), clientaddr.Port());
        // 将这个新获取的sockfd加入到红黑树中
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sockfd;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (n < 0)
        {
            LOG(ERROR, "新增sockfd:%d 事件到红黑树中失败\n", sockfd);
            return;
        }
        LOG(DEBUG, "新增sockfd:%d 事件到红黑树中成功\n", sockfd);
    }
    // 普通sockfd---通信
    void ServiceIO(int pos)
    {
        int sockfd = _revs[pos].data.fd;
        char buffer[1024]; // 缓冲区
        // 这里只保证recv一次不阻塞
        int n = ::recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            // 读取成功
            buffer[n] = '\0';
            cout << "server收到client发送的信息:" << buffer << endl;
            string send_str = "[Server Echo] # ";
            send_str += buffer;
            send(sockfd, send_str.c_str(), send_str.size(), 0);
        }
        else if (n == 0)
        {
            // 客户端退出
            // 将红黑树中的事件节点给删除掉
            // ！！！这里要先删除红黑树中节点，再关闭文件描述符。删除时fd必须为合法fd，非法fd会报错
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            // 关闭此sockfd
            ::close(sockfd);
            LOG(DEBUG, "客户端fd:%d 退出,已删除管理在红黑树中的事件,并关闭了此sockfd\n", sockfd);
        }
        else
        {
            // 读取失败
            // 将红黑树中的事件节点给删除掉
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            // 关闭此sockfd
            ::close(sockfd);
            LOG(DEBUG, "读取信息失败,已删除管理在红黑树中的事件,并关闭了此sockfd\n");
        }
    }

    // 处理就绪事件
    void handerEvent(int num)
    {
        // 一共num个事件
        for (int i = 0; i < num; i++)
        {
            int sockfd = _revs[i].data.fd; // 文件描述符
            // data联合体中，会将曾经设置过的sockfd，按照原样的返回回来，所以，才可以准确的拿到sockfd
            uint32_t revent = _revs[i].events; // 事件集合
            if (revent & EPOLLIN > 0)
            {
                // 读事件就绪
                if (sockfd == _listsockfd->SockFd())
                {
                    // listen sock---建立连接
                    AcceptClient();
                }
                else
                {
                    // 普通sockfd---通信
                    ServiceIO(i);
                }
            }
            if (revent & EPOLLOUT > 0)
            {
                // 写事件就绪
            }
        }
    }

    // 启动
    void Loop()
    {
        int timeout = 1000; // 等待策略
        while (true)
        {
            int n = epoll_wait(_epfd, _revs, gnum, timeout);
            switch (n)
            {
            case 0:
            {
                // 当前没有事件就绪,epoll_wait超时了
                // LOG(DEBUG, "epoll_wait函数超时了\n");
                break;
            }
            case -1:
            {
                // epoll_wait函数错误
                LOG(ERROR, "epoll_wait函数出错\n");
                break;
            }
            default:
            {
                LOG(DEBUG, "epoll_wait:已有事件就绪,就绪事件个数:%d\n", n);
                // 处理就绪事件
                handerEvent(n);
                break;
            }
            }
        }
    }

    ~epollServer()
    {
        _listsockfd->Close();
        if (_epfd >= 0)
        {
            LOG(DEBUG, "该epoll模型已被销毁 epfd:%d\n", _epfd);
            ::close(_epfd);
        }
    }

private:
    int _port;                      // 端口号
    unique_ptr<Socket> _listsockfd; // 套接字对象
    int _epfd;                      // epoll文件描述符
    struct epoll_event _revs[gnum]; // 缓冲区
};